/* Monom.h header file
   -------------------
   specifies class properties and methods
 */

#ifndef _MONOM_H
#define _MONOM_H

#include <iostream>
#include <math.h>       
#include <assert.h>

using namespace std;


class Monom {
    friend ostream& operator<<(ostream &out, const Monom &m);         // Operator for 'cout << monom'
    friend const Monom operator*(const double &num, const Monom &m2) { // Arithmetic multiplication <double> * Monom
       	return Monom(m2.C*num,m2.m);
    };

public:
    // Constructors
	Monom(const double coef=0,const int expo=0): C(coef) {          // Constructor with default values (0,0)
        m = ((coef == 0) ? 0 : expo);
    };
	Monom(const Monom &m): C(m.C), m(m.m) {};                       // Copy Constructor

    // non-const methods
    const Monom& operator=(const Monom &m2) {                       // Assignment operator --> Monom = Monom
        C = m2.C;
        m = m2.m;
        return (*this);
    };
    const Monom& derive() {                                         // Method for derivative
        C*=m;
        m = ((C == 0) ? 0 : m-1);
        return (*this);
    };
    void setC(const double &num) {                                  // Coefficient setter
        this->C = num;
        m = ((C == 0) ? 0 : m);
    };

    const int getExponent() const { return ((C == 0) ? 0 : m); };   // Method for getting 'm' power
	const double getC() const { return C; };						// Method for getting 'C' scalar

    // Logic bit-wise operators
    bool operator!=(const Monom &m2) const {                        // Operator for != comparison
        return (this->C != m2.C || this->m != m2.m);
    };
    bool operator==(const Monom &m2) const {                        // Operator for == comparison
        return (this->C == m2.C && this->m == m2.m);
    };

    // Arithmetic operators
    const Monom operator^(const int &num) const {                   // Power operation: Monom^scalar
        return Monom(pow(C,num),m*num);
    };
    const Monom operator/(const Monom &m2) const {                  // Arithmetic division: Monom / Monom
        assert(m2.C != 0);                                          // Division by 0 ?
        return Monom((C / m2.C),(m - m2.m));
    };
    const Monom operator/(const double &num) const {                // Arithmetic division: Monom / scalar
        assert(num != 0);                                           // Division by 0 ?
        return Monom((C / num),m);
    };
    const Monom operator*(const Monom &m2) const {                  // Arithmetic multiplication: Monom * Monom
        return Monom((this->C * m2.C),(this->m + m2.m));
    };
    const Monom operator*(const double &num) const {                // Arithmetic multiplication: Monom * scalar
        return Monom((C * num),m);
    };
    Monom& operator*=(const Monom &m2) {                            // Arithmetic multiplication *= --> Monom*=Monom
	    this->C *= m2.C;
	    this->m += m2.m;
        return (*this);
    };
    const Monom operator+(const Monom &m2) const {                  // Arithmetic addition: Monom + Monom
        assert (this->m == m2.m);   // error handler
        return Monom(C + m2.C,m);
    };
    Monom& operator+=(const double &num) {                          // Arithmetic addition += --> Monom+= scalar
        assert(this->m == 0);
        this->C += num;
        return (*this);
    };
    const Monom operator-(const Monom &m2) const {                  // Arithmetic subtraction: Monom - Monom
        assert (this->m == m2.m);   // error handler
        return Monom(this->C - m2.C,m);
    };
    const double operator()(const double &num) const {              // Operator for () C*num^m calculation
        return (C*pow(num,m));
    };
    bool operator>(const Monom &m2) const { return (this->m > m2.m); }; // Arithmetic comparison: Monom > Monom

private:
	double C;
	int m;
};


#endif
